﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using AlgorithmStarter;
using Phidgets;
using System.Reflection;
using NPlot;
using Algoritmi;

namespace RemoteMeter
{
    public partial class Form1 : Form
    {
        Random rand = new Random((int) DateTime.Now.Ticks);
        private bool DrawPlots = false;
        private int t_interval = 200;

        bool autoTestSet = false;
        int testAttivo = 0;
        string[] elencoTest = new string[] {
            "Energon", "k-Energon"
        };

        public Form1()
        {
            InitializeComponent();
            commManager = new CommunicationsManager("localhost", 12345);
            commManager.ConnessioneStabilita += new CommunicationsManager.ConnessioneHandler(commManager_ConnessioneStabilita);
            commManager.ConnessioneChiusa += new CommunicationsManager.ConnessioneHandler(commManager_ConnessioneChiusa);
            commManager.RicevutoMessaggio += new CommunicationsManager.MessaggioHandler(commManager_RicevutoMessaggio);


            try
            {
                ikit = new InterfaceKit();
                //ikit.Attach += new Phidgets.Events.AttachEventHandler(ikit_Attach);
                //ikit.Detach += new Phidgets.Events.DetachEventHandler(ikit_Detach);
                ikit.SensorChange += new Phidgets.Events.SensorChangeEventHandler(ikit_SensorChange);
                openCmdLine(ikit);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

            sensors = new List<Sensor>();
            
            //string sensorName1 = String.Format("Memory");
            //TabPage newPage1 = new TabPage(sensorName1);
            //Sensor sensor1 = new Sensor(sensorName1);
            //sensors.Add(sensor1);
            //newPage1.Controls.Add(sensor1.panel);
            //sensor1.panel.Dock = DockStyle.Fill;
            //tabControl1.TabPages.Add(newPage1);

            //string sensorName2 = String.Format("CPU");
            //TabPage newPage2 = new TabPage(sensorName2);
            //Sensor sensor2 = new Sensor(sensorName2);
            //sensor2.SetV(5);
            //sensors.Add(sensor2);
            //newPage2.Controls.Add(sensor2.panel);
            //sensor2.panel.Dock = DockStyle.Fill;
            //tabControl1.TabPages.Add(newPage2);

            //string sensorName3 = String.Format("HD 12V");
            //TabPage newPage3 = new TabPage(sensorName3);
            //Sensor sensor3 = new Sensor(sensorName3);
            //sensor3.SetV(12);
            //sensors.Add(sensor3);
            //newPage3.Controls.Add(sensor3.panel);
            //sensor3.panel.Dock = DockStyle.Fill;
            //tabControl1.TabPages.Add(newPage3);

            //string sensorName4 = String.Format("HD 5V");
            //TabPage newPage4 = new TabPage(sensorName4);
            //Sensor sensor4 = new Sensor(sensorName4);
            //sensor4.SetV(5);
            //sensors.Add(sensor4);
            //newPage4.Controls.Add(sensor4.panel);
            //sensor4.panel.Dock = DockStyle.Fill;
            //tabControl1.TabPages.Add(newPage4);

            string sensorName5 = String.Format("220");
            TabPage newPage5 = new TabPage(sensorName5);
            Sensor sensor5 = new Sensor(sensorName5);
            sensor5.DC = false;
            sensor5.SetV(220);
            sensors.Add(sensor5);
            newPage5.Controls.Add(sensor5.panel);
            sensor5.panel.Dock = DockStyle.Fill;
            tabControl1.TabPages.Add(newPage5);

            tabControl1.Refresh();

        }


        List<Sensor> sensors = new List<Sensor>();

        bool programRunning = false;

        void ikit_SensorChange(object sender, Phidgets.Events.SensorChangeEventArgs e)
        {
            if (e.Index < sensors.Count)
            {
                if (IgnorePhase != true)
                {
                    if (e.Index < sensors.Count)
                    {
                        sensors[e.Index].lastRead = e.Value;
                        sensors[e.Index].readings.Add(e.Value);
                    }
                }
            }
        }


        //Parses command line arguments and calls the appropriate open
        #region Command line open functions
        private void openCmdLine(Phidget p)
        {
            openCmdLine(p, null);
        }
        private void openCmdLine(Phidget p, String pass)
        {
            int serial = -1;
            int port = 5001;
            String host = null;
            bool remote = false, remoteIP = false;
            string[] args = Environment.GetCommandLineArgs();
            String appName = args[0];

            try
            { //Parse the flags
                for (int i = 1; i < args.Length; i++)
                {
                    if (args[i].StartsWith("-"))
                        switch (args[i].Remove(0, 1).ToLower())
                        {
                            case "n":
                                serial = int.Parse(args[++i]);
                                break;
                            case "r":
                                remote = true;
                                break;
                            case "s":
                                remote = true;
                                host = args[++i];
                                break;
                            case "p":
                                pass = args[++i];
                                break;
                            case "i":
                                remoteIP = true;
                                host = args[++i];
                                if (host.Contains(":"))
                                {
                                    host = host.Split(':')[0];
                                    port = int.Parse(host.Split(':')[1]);
                                }
                                break;
                            default:
                                goto usage;
                        }
                    else
                        goto usage;
                }

                if (remoteIP)
                    p.open(serial, host, port, pass);
                else if (remote)
                    p.open(serial, host, pass);
                else
                    p.open(serial);
                return; //success
            }
            catch { }
        usage:
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Invalid Command line arguments." + Environment.NewLine);
            sb.AppendLine("Usage: " + appName + " [Flags...]");
            sb.AppendLine("Flags:\t-n   serialNumber\tSerial Number, omit for any serial");
            sb.AppendLine("\t-r\t\tOpen remotely");
            sb.AppendLine("\t-s   serverID\tServer ID, omit for any server");
            sb.AppendLine("\t-i   ipAddress:port\tIp Address and Port. Port is optional, defaults to 5001");
            sb.AppendLine("\t-p   password\tPassword, omit for no password" + Environment.NewLine);
            sb.AppendLine("Examples: ");
            sb.AppendLine(appName + " -n 50098");
            sb.AppendLine(appName + " -r");
            sb.AppendLine(appName + " -s myphidgetserver");
            sb.AppendLine(appName + " -n 45670 -i 127.0.0.1:5001 -p paswrd");
            MessageBox.Show(sb.ToString(), "Argument Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

            Application.Exit();
        }
        #endregion

        Timer t;

        InterfaceKit ikit;

        CommunicationsManager commManager;

        private delegate void SetMsg();
        private string msg = "";
        private void SetMessage()
        {
            labelMessages.Text = msg;
        }

        private void CallSafeSetMessage(string mex)
        {
            msg = mex;
            labelMessages.Invoke(new SetMsg(SetMessage));
        }

        void commManager_RicevutoMessaggio(AlgorithmStarter.Message messaggio)
        {
            string str = "Ricevuto Messaggio";
            if (messaggio is AlgorithmStarter.IgnorePhase)
            {
                IgnorePhase = true;
                //StopMisurazione();
                str = "Ignore Phase is starting";

            }
            if (messaggio is AlgorithmStarter.IgnorePhaseCompleted)
            {
                IgnorePhase = false;
                //StartMisurazione();
                str = "IgnorePhaseCompleted";
            }
            if (messaggio is AlgorithmStarter.RunPhase)
            {
                ProgramRunning();
                str = "RunPhase";
            }
            if (messaggio is AlgorithmStarter.RunPhaseCompleted)
            {
                ProgramNotRunning();
                str = "RunPhaseCompleted";
            }
            if (messaggio is AlgorithmStarter.Error)
            {
                str = "Error";
            }
            CallSafeSetMessage(str);

        }

        void commManager_ConnessioneChiusa()
        {
            CallSafeSetMessage("Connessione Chiusa");
        }

        void commManager_ConnessioneStabilita()
        {
            CallSafeSetMessage("Connessione Aperta");
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            comboBoxAlgoritmo.Items.Clear();
            comboBoxAlgoritmo.Items.Add("Energon");
            comboBoxAlgoritmo.Items.Add("k-Energon");
            comboBoxAlgoritmo.Items.Add("f");
            comboBoxAlgoritmo.Items.Add("g");
            comboBoxAlgoritmo.Items.Add("f_dot_g");
            comboBoxAlgoritmo.Items.Add("f_plus_g");
            comboBoxAlgoritmo.Items.Add("SimpleLoopCSharp");
            comboBoxAlgoritmo.Items.Add("SimpleLoopCSharpTest");
            comboBoxAlgoritmo.SelectedIndex = 0;
            
        }

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            commManager.HostName = textBoxHostName.Text;
            commManager.Port = (int) numericUpDownPort.Value;
            commManager.ApriConnessione();
        }

        private void buttonMessaggio_Click(object sender, EventArgs e)
        {
            maxAttempt = (int) numericUpDownCount.Value;
            autoTestSet = false;
            AvviaSetTests();
        }

        System.Timers.Timer timer;

        private void SetControlsState(bool state)
        {
            numericUpDownCount.Enabled = state;
            numericUpDownSecondsAfter.Enabled = state;
            numericUpDownSecondsBefore.Enabled = state;
            numericUpDownSecondsBetween.Enabled = state;
            buttonConnect.Enabled = state;
            buttonDisconnect.Enabled = state;
            buttonStart.Enabled = state;
            buttonStop.Enabled = state;
            numericUpDownPort.Enabled = state;
        }

        private void AvviaSetTests()
        {
            progressBar1.Value = 0;
            //V = (double) numericUpDownV.Value;
            foreach (Sensor sensor in sensors)
            {
                sensor.AvviaSetTests();
            }
            if (autoTestSet)
            {
                algorithmName = elencoTest[testAttivo];
            } else
            {
                algorithmName = comboBoxAlgoritmo.SelectedItem.ToString();
            }
            //dataManager = new DataManager(algorithmName); 
            IstanziaAlgoritmo();
            ResetValori();
            SetControlsState(false);
            timer = new System.Timers.Timer();
            timer.AutoReset = false;
            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            timer.Interval = (double) (numericUpDownSecondsBefore.Value * 1000);
            timer.Start();
            StartMisurazione();
            nAttempt = 0;
        }

        DataManager dataManager;
        string algorithmName;
        double mediaDurata;
        double varianzaDurata;
        List<double> valoriDurate;

        private void ResetValori()
        {
            mediaDurata = 0.0;
            varianzaDurata = 0.0;
            foreach (Sensor sensor in sensors)
            {
                sensor.mediaCarico = 0.0;
                sensor.mediaScarico = 0.0;
                sensor.varianzaCarico = 0.0;
                sensor.varianzaScarico = 0.0;
                sensor.valoriScarico = new List<double>();
                sensor.valoriCarico = new List<double>();
            }
            valoriDurate = new List<double>();
        }

        private void FinalizzaValori()
        {
            foreach (Sensor sensor in sensors)
            {

                // calcolo la media
                sensor.mediaScarico = 0.0;
                foreach (double item in sensor.valoriScarico)
                {
                    sensor.mediaScarico += item;
                }
                sensor.mediaScarico /= (double)sensor.valoriScarico.Count;

                // la varianza
                sensor.varianzaScarico = 0.0;
                foreach (double item in sensor.valoriScarico)
                {
                    sensor.varianzaScarico += ((item - sensor.mediaScarico) * (item - sensor.mediaScarico));
                }
                sensor.varianzaScarico /= Math.Max((double)(sensor.valoriScarico.Count - 1), 1.0);

                // calcolo la media
                sensor.mediaCarico = 0.0;
                foreach (double item in sensor.valoriCarico)
                {
                    sensor.mediaCarico += item;
                }
                sensor.mediaCarico /= (double)sensor.valoriCarico.Count;

                // la varianza
                sensor.varianzaCarico = 0.0;
                foreach (double item in sensor.valoriCarico)
                {
                    sensor.varianzaCarico += ((item - sensor.mediaCarico) * (item - sensor.mediaCarico));
                }
                sensor.varianzaCarico /= Math.Max((double)(sensor.valoriCarico.Count - 1), 1.0);

            }
            // calcolo la media
            mediaDurata = 0.0;
            foreach (double item in valoriDurate)
            {
                mediaDurata += item;
            }
            mediaDurata /= Math.Max((double)valoriDurate.Count, 1.0) ;

            // la varianza
            varianzaDurata = 0.0;
            foreach (double item in valoriDurate)
            {
                varianzaDurata += ((item - mediaDurata) * (item - mediaDurata));
            }
            varianzaDurata /= Math.Max((double)(valoriDurate.Count - 1), 1.0);

            //double[] ammeters = new double[sensors.Count*2];
            //int i2 = 0;
            //for (int i = 0; i < sensors.Count; i++)
            //{
            //    ammeters[i2] = sensors[i].mediaScarico;
            //    i2++;
            //    ammeters[i2] = sensors[i].mediaCarico;
            //    i2++;
            //}

            List<double[]> setAmmeter = new List<double[]>();
            for (int j = 0; j < maxAttempt; j++)
            {
                double[] ammeters = new double[sensors.Count * 2];
                int i2 = 0;
                for (int i = 0; i < sensors.Count; i++)
                {
                    ammeters[i2] = sensors[i].valoriScarico[j];
                    i2++;
                    ammeters[i2] = sensors[i].valoriCarico[j];
                    i2++;
                }
                setAmmeter.Add(ammeters);
            }

            dataManager.AddTest(
                activeParams, 
                valoriDurate,
                setAmmeter);
        }

        private void SalvaValori()
        {
            //SaveFileDialog fd = new SaveFileDialog();
            //if (fd.ShowDialog() == DialogResult.OK)
            //{
            //    DataSet ds = new DataSet();
            //    DataTable dt = new DataTable("Tests");
            //    dt.Columns.Add("Scarico", typeof(double));
            //    dt.Columns.Add("Carico", typeof(double));
                
            //    ds.Tables.Add(dt);
            //    ds.WriteXml(fd.FileName, XmlWriteMode.IgnoreSchema);
            //}
        }

        int nAttempt = 0;
        int maxAttempt = 0;
        int step = 0;
        int maxStep;
        object[] activeParams;
        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (nAttempt < numericUpDownCount.Value)
            {
                //if (nAttempt > 0)
                //{
                //    FinalizzaValori();
                //    ResetValori();
                //}
                ImpostaAlgoritmo();
                nAttempt++;
            }
            else
            {
                FinalizzaValori();
                AggiornaProgressBar();
                if (step < maxStep)
                {
                    nAttempt = 0;
                    step++;
                    ResetValori();
                    timer_Elapsed(sender, e);
                }
                else
                {
                    //label_text.Invoke(new boolDelegate(SetControlsState), new object[] { true });
                    FineSessioneDiTest();
                    testAttivo++;
                    if (autoTestSet && testAttivo < elencoTest.Length)
                    {
                        step = 0;
                        AvviaSetTests();
                    }
                }
                //SetControlsState(true);
            }
        }
        private delegate void boolDelegate(bool b);
        private delegate void voidDelegate();

        private bool IgnorePhase = false;
        IAlgorithm alg = null;

        /// <summary>
        /// da chiamare all'inizio del set di test
        /// </summary>
        private void IstanziaAlgoritmo()
        {
            if (algorithmName == "Energon")
            {
                alg = new Energon();
            }
            if (algorithmName == "k-Energon")
            {
                alg = new KEnergon();
            }
            if (algorithmName == "SimpleLoopCSharp")
            {
                alg = new SimpleLoopCSharp();
            }
            if (algorithmName == "SimpleLoopCSharpTest")
            {
                alg = new SimpleLoopCSharpTest();
            }
            if (algorithmName == "f")
            {
                alg = new f();
            }
            if (algorithmName == "g")
            {
                alg = new g();
            }
            if (algorithmName == "f_plus_g")
            {
                alg = new f_plus_g();
            }
            if (algorithmName == "f_dot_g")
            {
                alg = new f_dot_g();
            }
            string[] names = new string[sensors.Count * 2];
            int i2 = 0;
            for (int i = 0; i < sensors.Count; i++)
            {
                names[i2] = sensors[i].sensorName + " O.S.";
                i2++;
                names[i2] = sensors[i].sensorName + " Alg";
                i2++;
            }
            dataManager = new DataManager(algorithmName, alg.getParametersNames(), names, maxAttempt);
        }

        private void InitSensors()
        {
            sensors = new List<Sensor>();
            // pulisco
            for (int i = tabControl1.TabPages.Count-1; i > 0; i--)
            {
                tabControl1.TabPages.RemoveAt(i);
            }
            // aggiungo
            for (int i = 0; i < (int) numericUpDownSensors.Value; i++)
            {
                string sensorName = String.Format("Sensor {0}", i+1);
                TabPage newPage = new TabPage(sensorName);
                Sensor sensor = new Sensor(sensorName);
                sensors.Add(sensor);
                newPage.Controls.Add(sensor.panel);
                sensor.panel.Dock = DockStyle.Fill;
                tabControl1.TabPages.Add(newPage);
            }
            tabControl1.Refresh();
        }

        /// <summary>
        /// da chiamare ogni volta che si esegu eun singolo test
        /// </summary>
        private void ImpostaAlgoritmo()
        {
            AlgorithmStarter.SetAlgorithm mex = new SetAlgorithm();
            mex.algorithmName = algorithmName;
            maxStep = alg.steps();
            //object[] parms = new object[2];
            //parms[0] = Convert.ToInt32(textBoxN.Text);
            //parms[1] = Convert.ToInt32(textBoxB.Text); ;
            //mex.algorithmParams = parms;
            activeParams = alg.getParameters(step);
            mex.algorithmParams = alg.getParameters(step);
            commManager.InviaMessaggio(mex);
        }

        private void tabPermutazioniLocali_Click(object sender, EventArgs e)
        {

        }


        private void StartMisurazione()
        {
            try
            {
                ikit.ratiometric = true;
                for (int i = 0; i < ikit.sensors.Count; i++)
                {
                    ikit.sensors[i].Sensitivity = 1;
                }
            }
            catch (Exception ex)
            {
            }
            t = new Timer();
            t.Tick += new EventHandler(t_Tick);
            t.Interval = t_interval;
            t.Start();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            StartMisurazione();
        }

        void t_Tick(object sender, EventArgs e)
        {
            if (IgnorePhase == true)
                return;
            foreach (Sensor sensor in sensors)
            {
                double val = 0;
                if (sensor.readings.Count > 0)
                {
                    foreach (int i in sensor.readings)
                    {
                        val += i;
                    }
                    val /= (double)sensor.readings.Count;
                }
                else
                {
                    val = sensor.lastRead;
                    //val = rand.Next(500, 600);
                }
                sensor.readings = new List<int>();
                double amp = 0.0;
                if (sensor.DC)
                {
                    amp = (val / 13.2) - 37.8787;
                }
                else
                {
                    amp = val * 0.04204;
                }
                double watt = amp * sensor.V;
                sensor.data.Add(watt);
                if (programRunning)
                {
                    sensor.running.Add(watt);
                }
                else
                {
                    sensor.notRunning.Add(watt);
                }
                if (DrawPlots)
                    sensor.PlotWave();
            }
        }

        private void TestFinished()
        {
            foreach (Sensor sensor in sensors)
            {
                double runningW = 0.0;
                foreach (double val in sensor.running)
                {
                    runningW += val;
                }
                runningW = runningW / (double)sensor.running.Count;

                sensor.valoriCarico.Add(runningW);

                double notRunningW = 0.0;
                foreach (double val in sensor.notRunning)
                {
                    notRunningW += val;
                }
                notRunningW = notRunningW / (double)sensor.notRunning.Count;

                sensor.valoriScarico.Add(notRunningW);

                sensor.running = new List<double>();
                sensor.notRunning = new List<double>();
            }
        }

        private double V = 3.3;
        private bool DC = true;

        private DateTime programStartTime;
        private DateTime programStopTime;


        private void StopMisurazione()
        {
            if (t != null)
                t.Stop();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            StopMisurazione();
        }
        private NPlot.Windows.PlotSurface2D plotSurface;

        //DataSet data = new DataSet();



        private void ProgramRunning()
        {
            programRunning = true;
            //buttonProgram.Text = "Program not running";
            //buttonProgram.Invalidate();
            programStartTime = DateTime.Now;
            foreach (Sensor sensor in sensors)
            {
                sensor.programStartArray.Add(sensor.data.Count);

            }
        }


        private void ProgramNotRunning()
        {
            programRunning = false;
            //buttonProgram.Text = "Program running";
            //buttonProgram.Invalidate();
            programStopTime = DateTime.Now;
            foreach (Sensor sensor in sensors)
            {
                sensor.programStopArray.Add(sensor.data.Count);
            }
            if (nAttempt < (int) numericUpDownCount.Value)
            {
                timer.Interval = (double) (numericUpDownSecondsBetween.Value * 1000);
            }
            else
            {
                timer.Interval = (double)(numericUpDownSecondsAfter.Value * 1000);
            }
            timer.Start();
            TimeSpan span = programStopTime - programStartTime;
            valoriDurate.Add(span.TotalSeconds);

            TestFinished();

        }

        DateTime measurementStartTime;
        bool measuringSO = false;
        bool measuringSW = false;

        private void AvviaMisurazioneSO()
        {
            measuringSO = true;
            measurementStartTime = DateTime.Now;
        }

        private void FermaMisurazioneSO()
        {
            measuringSO = false;
            DateTime now = DateTime.Now;
        }

        private void AvviaMisurazioneSW()
        {
            measuringSW = true;
            measurementStartTime = DateTime.Now;
        }

        private void FermaMisurazioneSW()
        {
            measuringSW = false;
        }

        private void FineSessioneDiTest()
        {
            string filename = String.Format("test-{0}.xml", algorithmName);
            dataManager.SaveData(filename);
        }

        private delegate void AggiornaProgressBarDelegate();

        private void AggiornaProgressBar()
        {
            if (progressBar1.InvokeRequired)
            {
                progressBar1.Invoke(new AggiornaProgressBarDelegate(AggiornaProgressBar));
            }
            else
            {
                progressBar1.Minimum = 0;
                progressBar1.Maximum = maxStep;
                progressBar1.Value = step;
            }
        }

        private void buttonScrivi_Click(object sender, EventArgs e)
        {
            FineSessioneDiTest();
        }

        private void numericUpDownSensors_ValueChanged(object sender, EventArgs e)
        {
            InitSensors();
        }

        private void numericUpDownSecondsBetween_ValueChanged(object sender, EventArgs e)
        {

        }

        private void numericUpDownSecondsBefore_ValueChanged(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            maxAttempt = (int)numericUpDownCount.Value;
            autoTestSet = true;
            AvviaSetTests();
        }
    }
}
